home *** CD-ROM | disk | FTP | other *** search
Wrap
/***************************************************************************** IsoHex19_2.cpp Ernest S. Pazera 04OCT2000 Start a WIN32 Application Workspace, add in this file Requires the following libs: ddraw.lib, dxguid.lib Requires the following files: DDFuncs.h.cpp, GDICanvas.h/cpp, IsoMouseMap.h/cpp, IsoScroller.h/cpp, IsoTilePlotter.h/cpp, IsoTileWalker.h/cpp TileSet.h/cpp. IsoHexCore.h, IsoHexDefs.h IsoRenderer.h/cpp *****************************************************************************/ ////////////////////////////////////////////////////////////////////////////// //INCLUDES ////////////////////////////////////////////////////////////////////////////// #define WIN32_LEAN_AND_MEAN #include <windows.h> #include "DDFuncs.h" #include "TileSet.h" #include "IsoHexCore.h" #include "IsoRenderer.h" #include <list> ////////////////////////////////////////////////////////////////////////////// //DEFINES ////////////////////////////////////////////////////////////////////////////// //name for our window class #define WINDOWCLASS "ISOHEX19" //title of the application #define WINDOWTITLE "IsoHex 19-2" const int MAPWIDTH=40; const int MAPHEIGHT=40; //gamestates const int GS_IDLE=0;//waits for a keypress const int GS_STARTTURN=1;//starts a players turn const int GS_ENDTURN=2;//ends a players turn const int GS_NEXTUNIT=3;//finds the next unit to move const int GS_STARTMOVE=4;//starts moving the unit const int GS_DOMOVE=5;//moves the unit const int GS_ENDMOVE=6;//ends a unit move const int GS_NULLMOVE=7;//tells a unit to not move const int GS_SKIPMOVE=8;//temporarily skip the move const int GS_HOLDPOSITION=9;//fortify const int GS_CLICKSELECT=10;//when a click occurs, this gamestate is selected const int GS_CLICKCENTER=11;//click on a map location with none of the players units const int GS_CLICKSTACK=12;//click on a map location with at least two of the players units const int GS_PICKUNIT=13;//pick unit from the selection window ////////////////////////////////////////////////////////////////////////////// //TYPEDEFS/STRUCTS ////////////////////////////////////////////////////////////////////////////// struct UnitInfo//unit information structure { int iType;//type of unit int iTeam;//team to which the unit belongs POINT ptPosition;//map location of the unit int iMovePoints;//number of movepoints left this turn bool bHolding;//true if holding position }; typedef UnitInfo *PUNITINFO;//pointer type alias for unitinfo typedef std::list<PUNITINFO> UNITLIST;//list of units typedef std::list<PUNITINFO>::iterator UNITLISTITER;//iterator for unit list ////////////////////////////////////////////////////////////////////////////// //PROTOTYPES ////////////////////////////////////////////////////////////////////////////// bool Prog_Init();//game data initalizer void Prog_Loop();//main game loop void Prog_Done();//game clean up //minimap functions void SetupMiniMap();//does initial setup of the minimap void UpdateMiniMap();//updates the minimap to reflect current game status void RedrawMiniMap();//redraws the minimap to reflect current gamestate void ShowMiniMap();//shows the minimap on-screen void DestroyMiniMap();//cleans up the minimap ////////////////////////////////////////////////////////////////////////////// //GLOBALS ////////////////////////////////////////////////////////////////////////////// HINSTANCE hInstMain=NULL;//main application handle HWND hWndMain=NULL;//handle to our main window //game state int iGameState=GS_IDLE; int iCurrentTeam=0;//the team whose turn it currently is //directdraw LPDIRECTDRAW7 lpdd=NULL; LPDIRECTDRAWSURFACE7 lpddsMain=NULL; LPDIRECTDRAWSURFACE7 lpddsBack=NULL; LPDIRECTDRAWSURFACE7 lpddsFrame=NULL; LPDIRECTDRAWSURFACE7 lpddsMiniMap=NULL;//minimap surface //tilesets CTileSet tsBack;//background CTileSet tsUnit;//unit CTileSet tsShield;//shields CTileSet tsPressEnter;//press enter text CTileSet tsMiniMap;//minimap tileset //offsets for the shields POINT ptShieldOffset[2];//one for each unit //isohexcore components CTilePlotter TilePlotter;//plotter CTileWalker TileWalker;//walker CScroller Scroller;//scroller CMouseMap MouseMap;//mousemap CRenderer Renderer;//renderer //minimap components CTilePlotter MiniTilePlotter; CScroller MiniScroller; POINT ptScroll;//keep track of how quickly we scroll POINT ptClick;//keep track of the clicked position //map location structure struct MapLocation { UNITLIST ulUnitList;//list of units on this map location }; MapLocation mlMap[MAPWIDTH][MAPHEIGHT];//map array UNITLIST MainUnitList;//unit list for all units UNITLIST TeamUnitList;//unit list for teams(Current Players Turn) PUNITINFO pCurrentUnit;//current unit being moved bool bFlash;//controls the flashing of the current unit ISODIRECTION idMoveUnit;//direction in which the unit will be moved bool bMovedUnit;//indicates whether or not a unit has been moved by the current player this turn //unit selection variables(for selecting stacks of units) RECT rcSelectWindow;//the selection window POINT ptCellSize;//size of selection cell PUNITINFO SelectUnitList[20];//unit selection list (max of 20 units) POINT ptUnitOffset;//offset for placing units in the selection window DWORD dwSelectWindowColor;//color for the selection window //minimap array int* MiniMap;//current minimap array //rendering functionprototype void RenderFunc(LPDIRECTDRAWSURFACE7 lpddsDst,RECT* rcClip,int xDst,int yDst,int xMap,int yMap); ////////////////////////////////////////////////////////////////////////////// //WINDOWPROC ////////////////////////////////////////////////////////////////////////////// LRESULT CALLBACK TheWindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam) { //which message did we get? switch(uMsg) { case WM_LBUTTONDOWN://beginning of click-select { //process differently, depending on gamestate switch(iGameState) { case GS_IDLE: { //grab the mouse position POINT ptCursor; ptCursor.x=LOWORD(lParam); ptCursor.y=HIWORD(lParam); //use the mousemap to get click position ptClick=MouseMap.MapMouse(ptCursor); }break; } return(0);//handled }break; case WM_LBUTTONUP://end of click-select { //process differently, depending on gamestate switch(iGameState) { case GS_IDLE: { //grab the mouse position POINT ptCursor; ptCursor.x=LOWORD(lParam); ptCursor.y=HIWORD(lParam); //use the mousemap to get click position POINT ptMap=MouseMap.MapMouse(ptCursor); //check map position against ptClick if(ptMap.x==ptClick.x && ptMap.y==ptClick.y) { //set gamestate to GS_CLICKSELECT iGameState=GS_CLICKSELECT; } }break; case GS_PICKUNIT: { //grab the mouse position POINT ptMouse; ptMouse.x=LOWORD(lParam); ptMouse.y=HIWORD(lParam); //check to see if the click was within the select window if(PtInRect(&rcSelectWindow,ptMouse)) { //within the select window //determine which cell was clicked ptMouse.x-=rcSelectWindow.left;//subtract top left of the window ptMouse.y-=rcSelectWindow.top; ptMouse.x/=ptCellSize.x;//divide by cellsize ptMouse.y/=ptCellSize.y; int cellnum=ptMouse.x+5*ptMouse.y;//calc cell number //check for a NULL if(SelectUnitList[cellnum]==NULL) { //empty cell //do nothing return(0); } else { //non-empty cell //check if the unit has any movement points left if(SelectUnitList[cellnum]->iMovePoints==0) { //no movement points //do nothing return(0); } else { //check for holding position if(SelectUnitList[cellnum]->bHolding) { //set holding to false SelectUnitList[cellnum]->bHolding=false; } //remove this unit from the team list TeamUnitList.remove(SelectUnitList[cellnum]); //re-add this unit to the team list at the beginning TeamUnitList.push_front(SelectUnitList[cellnum]); //select next unit iGameState=GS_NEXTUNIT; //add entire screen to update rect Renderer.AddRect(Scroller.GetScreenSpace()); } } } else { //outside of select window iGameState=GS_NEXTUNIT; } }break; } return(0);//handled }break; case WM_KEYDOWN: { switch(wParam) { case VK_ESCAPE: { DestroyWindow(hWndMain); return(0); }break; case VK_RETURN://at end of turn, if no unit has been moved { if(iGameState==GS_IDLE && pCurrentUnit==NULL) iGameState=GS_ENDTURN; return(0); }break; case VK_SPACE://do not move unit { if(iGameState==GS_IDLE && pCurrentUnit!=NULL) iGameState=GS_NULLMOVE;//only respond when gamestate is GS_IDLE; return(0); }break; case 'W'://wait to move this unit later { if(iGameState==GS_IDLE && pCurrentUnit!=NULL) iGameState=GS_SKIPMOVE;//skip this unit for now }break; case 'H'://tell unit to hold position { if(iGameState==GS_IDLE && pCurrentUnit!=NULL) iGameState=GS_HOLDPOSITION;//tell the unit to hold position }break; case 'C'://center on the current unit { if(iGameState==GS_IDLE && pCurrentUnit!=NULL) { //plot the current unit's world position POINT ptPlot=TilePlotter.PlotTile(pCurrentUnit->ptPosition); //adjust by half width and height of the screen ptPlot.x-=(Scroller.GetScreenSpaceWidth()/2); ptPlot.y-=(Scroller.GetScreenSpaceHeight()/2); //set the anchor Scroller.SetAnchor(&ptPlot); //add update rect to renderer Renderer.AddRect(Scroller.GetScreenSpace()); } }break; case VK_NUMPAD8: case VK_UP: { if(iGameState==GS_IDLE && pCurrentUnit!=NULL)//gamestate must be GS_IDLE { idMoveUnit=ISO_NORTH;//move to the north POINT ptNext=TileWalker.TileWalk(pCurrentUnit->ptPosition,idMoveUnit);//check the next position if(ptNext.x>=0 && ptNext.y>=0 && ptNext.x<MAPWIDTH && ptNext.y<MAPWIDTH)//bounds checking { if(mlMap[ptNext.x][ptNext.y].ulUnitList.empty())//if the map location is empty { iGameState=GS_STARTMOVE;//set the unit in motion } else { UNITLISTITER iter=mlMap[ptNext.x][ptNext.y].ulUnitList.begin();//get the first entry in the list PUNITINFO pUnitInfo=*iter;//get the unit from the list if(pUnitInfo->iTeam==pCurrentUnit->iTeam)//must be the same team { iGameState=GS_STARTMOVE; } } } } }break; case VK_NUMPAD9: case VK_PRIOR: { if(iGameState==GS_IDLE && pCurrentUnit!=NULL)//gamestate must be GS_IDLE { idMoveUnit=ISO_NORTHEAST;//move to the north POINT ptNext=TileWalker.TileWalk(pCurrentUnit->ptPosition,idMoveUnit);//check the next position if(ptNext.x>=0 && ptNext.y>=0 && ptNext.x<MAPWIDTH && ptNext.y<MAPWIDTH)//bounds checking { if(mlMap[ptNext.x][ptNext.y].ulUnitList.empty())//if the map location is empty { iGameState=GS_STARTMOVE;//set the unit in motion } else { UNITLISTITER iter=mlMap[ptNext.x][ptNext.y].ulUnitList.begin();//get the first entry in the list PUNITINFO pUnitInfo=*iter;//get the unit from the list if(pUnitInfo->iTeam==pCurrentUnit->iTeam)//must be the same team { iGameState=GS_STARTMOVE; } } } } }break; case VK_NUMPAD6: case VK_RIGHT: { if(iGameState==GS_IDLE && pCurrentUnit!=NULL)//gamestate must be GS_IDLE { idMoveUnit=ISO_EAST;//move to the north POINT ptNext=TileWalker.TileWalk(pCurrentUnit->ptPosition,idMoveUnit);//check the next position if(ptNext.x>=0 && ptNext.y>=0 && ptNext.x<MAPWIDTH && ptNext.y<MAPWIDTH)//bounds checking { if(mlMap[ptNext.x][ptNext.y].ulUnitList.empty())//if the map location is empty { iGameState=GS_STARTMOVE;//set the unit in motion } else { UNITLISTITER iter=mlMap[ptNext.x][ptNext.y].ulUnitList.begin();//get the first entry in the list PUNITINFO pUnitInfo=*iter;//get the unit from the list if(pUnitInfo->iTeam==pCurrentUnit->iTeam)//must be the same team { iGameState=GS_STARTMOVE; } } } } }break; case VK_NUMPAD3: case VK_NEXT: { if(iGameState==GS_IDLE && pCurrentUnit!=NULL)//gamestate must be GS_IDLE { idMoveUnit=ISO_SOUTHEAST;//move to the north POINT ptNext=TileWalker.TileWalk(pCurrentUnit->ptPosition,idMoveUnit);//check the next position if(ptNext.x>=0 && ptNext.y>=0 && ptNext.x<MAPWIDTH && ptNext.y<MAPWIDTH)//bounds checking { if(mlMap[ptNext.x][ptNext.y].ulUnitList.empty())//if the map location is empty { iGameState=GS_STARTMOVE;//set the unit in motion } else { UNITLISTITER iter=mlMap[ptNext.x][ptNext.y].ulUnitList.begin();//get the first entry in the list PUNITINFO pUnitInfo=*iter;//get the unit from the list if(pUnitInfo->iTeam==pCurrentUnit->iTeam)//must be the same team { iGameState=GS_STARTMOVE; } } } } }break; case VK_NUMPAD2: case VK_DOWN: { if(iGameState==GS_IDLE && pCurrentUnit!=NULL)//gamestate must be GS_IDLE { idMoveUnit=ISO_SOUTH;//move to the north POINT ptNext=TileWalker.TileWalk(pCurrentUnit->ptPosition,idMoveUnit);//check the next position if(ptNext.x>=0 && ptNext.y>=0 && ptNext.x<MAPWIDTH && ptNext.y<MAPWIDTH)//bounds checking { if(mlMap[ptNext.x][ptNext.y].ulUnitList.empty())//if the map location is empty { iGameState=GS_STARTMOVE;//set the unit in motion } else { UNITLISTITER iter=mlMap[ptNext.x][ptNext.y].ulUnitList.begin();//get the first entry in the list PUNITINFO pUnitInfo=*iter;//get the unit from the list if(pUnitInfo->iTeam==pCurrentUnit->iTeam)//must be the same team { iGameState=GS_STARTMOVE; } } } } }break; case VK_NUMPAD1: case VK_END: { if(iGameState==GS_IDLE && pCurrentUnit!=NULL)//gamestate must be GS_IDLE { idMoveUnit=ISO_SOUTHWEST;//move to the north POINT ptNext=TileWalker.TileWalk(pCurrentUnit->ptPosition,idMoveUnit);//check the next position if(ptNext.x>=0 && ptNext.y>=0 && ptNext.x<MAPWIDTH && ptNext.y<MAPWIDTH)//bounds checking { if(mlMap[ptNext.x][ptNext.y].ulUnitList.empty())//if the map location is empty { iGameState=GS_STARTMOVE;//set the unit in motion } else { UNITLISTITER iter=mlMap[ptNext.x][ptNext.y].ulUnitList.begin();//get the first entry in the list PUNITINFO pUnitInfo=*iter;//get the unit from the list if(pUnitInfo->iTeam==pCurrentUnit->iTeam)//must be the same team { iGameState=GS_STARTMOVE; } } } } }break; case VK_NUMPAD4: case VK_LEFT: { if(iGameState==GS_IDLE && pCurrentUnit!=NULL)//gamestate must be GS_IDLE { idMoveUnit=ISO_WEST;//move to the north POINT ptNext=TileWalker.TileWalk(pCurrentUnit->ptPosition,idMoveUnit);//check the next position if(ptNext.x>=0 && ptNext.y>=0 && ptNext.x<MAPWIDTH && ptNext.y<MAPWIDTH)//bounds checking { if(mlMap[ptNext.x][ptNext.y].ulUnitList.empty())//if the map location is empty { iGameState=GS_STARTMOVE;//set the unit in motion } else { UNITLISTITER iter=mlMap[ptNext.x][ptNext.y].ulUnitList.begin();//get the first entry in the list PUNITINFO pUnitInfo=*iter;//get the unit from the list if(pUnitInfo->iTeam==pCurrentUnit->iTeam)//must be the same team { iGameState=GS_STARTMOVE; } } } } }break; case VK_NUMPAD7: case VK_HOME: { if(iGameState==GS_IDLE && pCurrentUnit!=NULL)//gamestate must be GS_IDLE { idMoveUnit=ISO_NORTHWEST;//move to the north POINT ptNext=TileWalker.TileWalk(pCurrentUnit->ptPosition,idMoveUnit);//check the next position if(ptNext.x>=0 && ptNext.y>=0 && ptNext.x<MAPWIDTH && ptNext.y<MAPWIDTH)//bounds checking { if(mlMap[ptNext.x][ptNext.y].ulUnitList.empty())//if the map location is empty { iGameState=GS_STARTMOVE;//set the unit in motion } else { UNITLISTITER iter=mlMap[ptNext.x][ptNext.y].ulUnitList.begin();//get the first entry in the list PUNITINFO pUnitInfo=*iter;//get the unit from the list if(pUnitInfo->iTeam==pCurrentUnit->iTeam)//must be the same team { iGameState=GS_STARTMOVE; } } } } }break; } }break; case WM_DESTROY://the window is being destroyed { //tell the application we are quitting PostQuitMessage(0); //handled message, so return 0 return(0); }break; case WM_PAINT://the window needs repainting { //a variable needed for painting information PAINTSTRUCT ps; //start painting HDC hdc=BeginPaint(hwnd,&ps); ///////////////////////////// //painting code would go here ///////////////////////////// //end painting EndPaint(hwnd,&ps); //handled message, so return 0 return(0); }break; } //pass along any other message to default message handler return(DefWindowProc(hwnd,uMsg,wParam,lParam)); } ////////////////////////////////////////////////////////////////////////////// //WINMAIN ////////////////////////////////////////////////////////////////////////////// int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd) { //assign instance to global variable hInstMain=hInstance; //create window class WNDCLASSEX wcx; //set the size of the structure wcx.cbSize=sizeof(WNDCLASSEX); //class style wcx.style=CS_OWNDC | CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; //window procedure wcx.lpfnWndProc=TheWindowProc; //class extra wcx.cbClsExtra=0; //window extra wcx.cbWndExtra=0; //application handle wcx.hInstance=hInstMain; //icon wcx.hIcon=LoadIcon(NULL,IDI_APPLICATION); //cursor wcx.hCursor=LoadCursor(NULL,IDC_ARROW); //background color wcx.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH); //menu wcx.lpszMenuName=NULL; //class name wcx.lpszClassName=WINDOWCLASS; //small icon wcx.hIconSm=NULL; //register the window class, return 0 if not successful if(!RegisterClassEx(&wcx)) return(0); //create main window hWndMain=CreateWindowEx(0,WINDOWCLASS,WINDOWTITLE, WS_POPUP | WS_VISIBLE,0,0,320,240,NULL,NULL,hInstMain,NULL); //error check if(!hWndMain) return(0); //if program initialization failed, then return with 0 if(!Prog_Init()) return(0); //message structure MSG msg; //message pump for(;;) { //look for a message if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { //there is a message //check that we arent quitting if(msg.message==WM_QUIT) break; //translate message TranslateMessage(&msg); //dispatch message DispatchMessage(&msg); } //run main game loop Prog_Loop(); } //clean up program data Prog_Done(); //return the wparam from the WM_QUIT message return(msg.wParam); } ////////////////////////////////////////////////////////////////////////////// //INITIALIZATION ////////////////////////////////////////////////////////////////////////////// bool Prog_Init() { //create IDirectDraw object lpdd=LPDD_Create(hWndMain,DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWREBOOT); //set display mode lpdd->SetDisplayMode(640,480,16,0,0); //create primary surface lpddsMain=LPDDS_CreatePrimary(lpdd,1); //get back buffer lpddsBack=LPDDS_GetSecondary(lpddsMain); //create the frame buffer lpddsFrame=LPDDS_CreateOffscreen(lpdd,640,480); //load in the mousemap MouseMap.Load("MouseMap.bmp"); //set up the tile plotter TilePlotter.SetMapType(ISOMAP_DIAMOND);//diamond mode TilePlotter.SetTileSize(MouseMap.GetWidth(),MouseMap.GetHeight());//grab width and height from mousemap //set up tile walker to diamond mode TileWalker.SetMapType(ISOMAP_DIAMOND); //set up screeen space RECT rcTemp; SetRect(&rcTemp,0,0,480,480); Scroller.SetScreenSpace(&rcTemp); //load in tiles and other images tsBack.Load(lpdd,"backgroundts.bmp"); tsUnit.Load(lpdd,"units.bmp"); tsShield.Load(lpdd,"shields.bmp"); tsPressEnter.Load(lpdd,"PressEnter.bmp"); //calculate the shield offsets ptShieldOffset[0].x=tsUnit.GetTileList()[0].rcSrc.right-tsUnit.GetTileList()[0].ptAnchor.x; ptShieldOffset[0].y=tsUnit.GetTileList()[0].rcSrc.top-tsUnit.GetTileList()[0].ptAnchor.y; ptShieldOffset[1].x=tsUnit.GetTileList()[1].rcSrc.right-tsUnit.GetTileList()[1].ptAnchor.x; ptShieldOffset[1].y=tsUnit.GetTileList()[1].rcSrc.top-tsUnit.GetTileList()[1].ptAnchor.y; //grab tile extent from tileset CopyRect(&rcTemp,&tsBack.GetTileList()[0].rcDstExt); //calculate the worldspace Scroller.CalcWorldSpace(&TilePlotter,&rcTemp,MAPWIDTH,MAPHEIGHT); //calculate the mousemap reference point MouseMap.CalcReferencePoint(&TilePlotter,&rcTemp); //calculate anchor space Scroller.CalcAnchorSpace(); //set wrap modes for scroller Scroller.SetHWrapMode(WRAPMODE_CLIP); Scroller.SetVWrapMode(WRAPMODE_CLIP); //set scroller anchor to (0,0) Scroller.GetAnchor()->x=0; Scroller.GetAnchor()->y=0; //attach scrolelr and tilewalker to mousemap MouseMap.SetScroller(&Scroller); MouseMap.SetTileWalker(&TileWalker); //set up the map to a random tilefield int x; int y; for(x=0;x<MAPWIDTH;x++) { for(y=0;y<MAPHEIGHT;y++) { mlMap[x][y].ulUnitList.clear();//clear out the list for this map location } } //place the units int team; int count; PUNITINFO pUnitInfo; POINT ptTest; for(team=0;team<2;team++)//place units for both teams { for(count=0;count<20;count++)//place 20 units for each team { //check for a valid placement bool found=false; while(!found) { //random placement ptTest.x=rand()%MAPWIDTH; ptTest.y=rand()%MAPHEIGHT; //check for a unit at that position if(mlMap[ptTest.x][ptTest.y].ulUnitList.empty()) { //unit list at location is empty... you can place the unit found=true; } else { //unit list is not empty, so must be occupied by the same team to be a valid location UNITLISTITER iter=mlMap[ptTest.x][ptTest.y].ulUnitList.begin();//get iterator to beginning of list pUnitInfo=*iter;//grab the items stored //check for the same team if(pUnitInfo->iTeam==team) { //same team, valid location found=true; } } } //create the unit pUnitInfo=new UnitInfo; pUnitInfo->iTeam=team;//team pUnitInfo->iType=rand()%2;//type pUnitInfo->bHolding=false;//not holding position pUnitInfo->ptPosition=ptTest;//location //place unit in the main list MainUnitList.push_back(pUnitInfo); //place unit on the map mlMap[pUnitInfo->ptPosition.x][pUnitInfo->ptPosition.y].ulUnitList.push_back(pUnitInfo); } } //calculate the extent rect RECT rcExtent; CopyRect(&rcExtent,&tsBack.GetTileList()[0].rcDstExt);//set to background extent UnionRect(&rcExtent,&rcExtent,&tsUnit.GetTileList()[0].rcDstExt);//union with unit extent rcExtent.right+=tsShield.GetTileList()[0].rcDstExt.right;//adjust the extent for the width of the shield //set up the renderer Renderer.SetBackBuffer(lpddsBack); Renderer.SetExtentRect(&rcExtent); Renderer.SetFrameBuffer(lpddsFrame); Renderer.SetMapSize(MAPWIDTH,MAPHEIGHT); Renderer.SetMouseMap(&MouseMap); Renderer.SetPlotter(&TilePlotter); Renderer.SetRenderFunction(RenderFunc); Renderer.SetScroller(&Scroller); Renderer.SetUpdateRectCount(100); Renderer.SetWalker(&TileWalker); //set the initial gamestate iGameState=GS_STARTTURN; iCurrentTeam=0;//current team TeamUnitList.clear();//clear out the team's unit list //set up the selection window variables DDPIXELFORMAT ddpf; DDPF_Clear(&ddpf); lpddsMain->GetPixelFormat(&ddpf);//grab pixel format ddpf.dwRBitMask=(ddpf.dwRBitMask*3/4)&(ddpf.dwRBitMask);//calculate 3/4 red ddpf.dwGBitMask=(ddpf.dwGBitMask*3/4)&(ddpf.dwGBitMask);//calc 3/4 green ddpf.dwBBitMask=(ddpf.dwBBitMask*3/4)&(ddpf.dwBBitMask);//calc 3/4 blue dwSelectWindowColor=ddpf.dwBBitMask | ddpf.dwRBitMask | ddpf.dwGBitMask;//make select window color //calculate the cell size RECT rcCell; CopyRect(&rcCell,&tsUnit.GetTileList()[0].rcDstExt); UnionRect(&rcCell,&rcCell,&tsUnit.GetTileList()[1].rcDstExt); rcCell.right+=(tsShield.GetTileList()[0].rcDstExt.right-tsShield.GetTileList()[0].rcDstExt.left); //cell size ptCellSize.x=rcCell.right-rcCell.left; ptCellSize.y=rcCell.bottom-rcCell.top; //unit offset ptUnitOffset.x=-rcCell.left; ptUnitOffset.y=-rcCell.top; //calculate select window rect SetRect(&rcSelectWindow,0,0,ptCellSize.x*5,ptCellSize.y*4); //center the select window OffsetRect(&rcSelectWindow,320-rcSelectWindow.right/2,240-rcSelectWindow.bottom/2); //update the entire screenspace Renderer.AddRect(Scroller.GetScreenSpace()); //setup the minimap SetupMiniMap(); UpdateMiniMap(); RedrawMiniMap(); ShowMiniMap(); return(true);//return success } ////////////////////////////////////////////////////////////////////////////// //CLEANUP ////////////////////////////////////////////////////////////////////////////// void Prog_Done() { //destroy minimap DestroyMiniMap(); //release frame buffer LPDDS_Release(&lpddsFrame); //release main/back surfaces LPDDS_Release(&lpddsMain); //release directdraw LPDD_Release(&lpdd); } ////////////////////////////////////////////////////////////////////////////// //MAIN GAME LOOP ////////////////////////////////////////////////////////////////////////////// void Prog_Loop() { switch(iGameState) { case GS_STARTTURN://start the current team's turn { PUNITINFO pUnitInfo;//variable to check for the team's units UNITLISTITER iter;//iterator for the main unit list for(iter=MainUnitList.begin();iter!=MainUnitList.end();iter++)//iterate through the main unit list { pUnitInfo=*iter;//grab the unit from the list if(pUnitInfo->iTeam==iCurrentTeam)//does this unit belong to the current team? { //give the unit a movement point pUnitInfo->iMovePoints=1+pUnitInfo->iType; //add this unit to the team list TeamUnitList.push_back(pUnitInfo); } } //set moved unit flag to false bMovedUnit=false; //set the next gamestate iGameState=GS_NEXTUNIT; }break; case GS_NEXTUNIT://select the next unit as the current unit { //set current unit to NULL pCurrentUnit=NULL; if(TeamUnitList.empty())//if the team unit list is empty { //if a unit has been moved, send to end of turn if(bMovedUnit) { iGameState=GS_ENDTURN;//end the turn } else { //send to GS_IDLE iGameState=GS_IDLE;//send to idle state } } else { //turn is not over UNITLISTITER iter=TeamUnitList.begin();//get the first unit in the team list pCurrentUnit=*iter;//grab the unit from the list TeamUnitList.pop_front();//remove the unit from the list //check to see if this unit is holding position if(pCurrentUnit->bHolding) return;//go to next unit mlMap[pCurrentUnit->ptPosition.x][pCurrentUnit->ptPosition.y].ulUnitList.remove(pCurrentUnit);//remove the unit from the map location mlMap[pCurrentUnit->ptPosition.x][pCurrentUnit->ptPosition.y].ulUnitList.push_front(pCurrentUnit);//place unit at the top of the map locations unit list POINT ptPlot=TilePlotter.PlotTile(pCurrentUnit->ptPosition);//plot the unit's location POINT ptScreen=Scroller.WorldToScreen(ptPlot);//translate into screen coordinates if(!PtInRect(Scroller.GetScreenSpace(),ptScreen))//check to see if point is within screenspace { //not on screen ptPlot.x-=(Scroller.GetScreenSpaceWidth()/2); ptPlot.y-=(Scroller.GetScreenSpaceHeight()/2); //set the anchor Scroller.SetAnchor(&ptPlot); Renderer.AddRect(Scroller.GetScreenSpace()); } iGameState=GS_IDLE;//set to idling gamestate } }break; case GS_ENDTURN://end of a player's turn { //clear out team unit list(just to be sure) TeamUnitList.clear(); //change team iCurrentTeam=1-iCurrentTeam; iGameState=GS_STARTTURN;//set gamestate to start next turn }break; case GS_NULLMOVE://do not move the current unit { //set the moved flag to true bMovedUnit=true; //set movement points to 0 pCurrentUnit->iMovePoints=0; //don't really do anything, just go to the next unit pCurrentUnit=NULL; iGameState=GS_NEXTUNIT; }break; case GS_SKIPMOVE://skip this unit for now { //put unit at end of team unit list TeamUnitList.push_back(pCurrentUnit); pCurrentUnit=NULL;//set current unit to NULL iGameState=GS_NEXTUNIT;//select the next unit }break; case GS_HOLDPOSITION://tell unit to hold position { //set holding flag pCurrentUnit->bHolding=true; //show the holding unit Renderer.AddTile(pCurrentUnit->ptPosition.x,pCurrentUnit->ptPosition.y); pCurrentUnit=NULL; bFlash=true; //set next gamestate iGameState=GS_NEXTUNIT; DWORD dwTimeStart=GetTickCount();//get the frame start time //scroll the frame (0,0) Renderer.ScrollFrame(0,0); //update the frame Renderer.UpdateFrame(); //flip to show the back buffer lpddsMain->Flip(0,DDFLIP_WAIT); //wait until 500 ms have passed while(GetTickCount()-dwTimeStart<500); }break; case GS_STARTMOVE: { //set the moved flag to true bMovedUnit=true; //remove the unit from the map location mlMap[pCurrentUnit->ptPosition.x][pCurrentUnit->ptPosition.y].ulUnitList.remove(pCurrentUnit); Renderer.AddTile(pCurrentUnit->ptPosition.x,pCurrentUnit->ptPosition.y); //set next gamestate iGameState=GS_DOMOVE; }break; case GS_DOMOVE: { //move the unit pCurrentUnit->ptPosition=TileWalker.TileWalk(pCurrentUnit->ptPosition,idMoveUnit); //set next gamestate iGameState=GS_ENDMOVE; }break; case GS_ENDMOVE: { //place the unit on its new map location mlMap[pCurrentUnit->ptPosition.x][pCurrentUnit->ptPosition.y].ulUnitList.push_front(pCurrentUnit); Renderer.AddTile(pCurrentUnit->ptPosition.x,pCurrentUnit->ptPosition.y); //subtract a movement point pCurrentUnit->iMovePoints--; //check to see if this unit is finished moving if(pCurrentUnit->iMovePoints==0) { //done moving pCurrentUnit=NULL; //set next gamestate iGameState=GS_NEXTUNIT; } else { //not done moving iGameState=GS_IDLE; } bFlash=true; DWORD dwTimeStart=GetTickCount();//get the frame start time //scroll the frame (0,0) Renderer.ScrollFrame(0,0); //update the frame Renderer.UpdateFrame(); //flip to show the back buffer lpddsMain->Flip(0,DDFLIP_WAIT); //wait until 500 ms have passed while(GetTickCount()-dwTimeStart<500); }break; case GS_IDLE://the game is idling, update the frame, but thats about it. { DWORD dwTimeStart=GetTickCount();//get the frame start time //scroll the frame (0,0) Renderer.ScrollFrame(0,0); //toggle unit flash bFlash=!bFlash; //if the current unit is not null if(pCurrentUnit!=NULL) { //add the tile in which the current unit lives Renderer.AddTile(pCurrentUnit->ptPosition.x,pCurrentUnit->ptPosition.y); } //update the frame Renderer.UpdateFrame(); //if the current unit IS null if(pCurrentUnit==NULL) { //show the end of turn marker if bflash is true if(bFlash) tsPressEnter.PutTile(lpddsBack,0,0,iCurrentTeam); } //redo the minimap UpdateMiniMap(); RedrawMiniMap(); ShowMiniMap(); //flip to show the back buffer lpddsMain->Flip(0,DDFLIP_WAIT); //wait until 200 ms have passed while(GetTickCount()-dwTimeStart<200); }break; case GS_CLICKSELECT: { //check map location for emptiness if(mlMap[ptClick.x][ptClick.y].ulUnitList.empty()) { //map location is empty iGameState=GS_CLICKCENTER;//we want to center on this map location } else { //map location is not empty //look at top of list UNITLISTITER iter=mlMap[ptClick.x][ptClick.y].ulUnitList.begin(); PUNITINFO pUnitInfo=*iter; //check that if this unit belongs to the current team if(pUnitInfo->iTeam==iCurrentTeam) { //belongs to current team //one unit? if(mlMap[ptClick.x][ptClick.y].ulUnitList.size()==1) { //a single unit(already contained in pUnitInfo) //is this the current unit? if(pUnitInfo==pCurrentUnit) { //this is the current unit iGameState=GS_IDLE;//return to the neutral gamestate } else { //this is not the current unit //does this unit have any movement points left? if(pUnitInfo->iMovePoints>0) { //has movement points left //push the current unit to front of team list if(pCurrentUnit) TeamUnitList.push_front(pCurrentUnit); pCurrentUnit=NULL; //set holding to false for the new unit pUnitInfo->bHolding=false; //remove new unit from team list TeamUnitList.remove(pUnitInfo); //put new unit in front of team list TeamUnitList.push_front(pUnitInfo); //go to gs_nextunit iGameState=GS_NEXTUNIT; } else { //does not have movement points left //go back to gs_idle iGameState=GS_IDLE; } } } else { //place the current unit at the front of the list if(pCurrentUnit) TeamUnitList.push_front(pCurrentUnit); //a stack of units iGameState=GS_CLICKSTACK; } } else { //does not belong to current team iGameState=GS_CLICKCENTER;//we want to center on this map location } } }break; case GS_CLICKCENTER: { //center on clicked tile POINT ptPlot=TilePlotter.PlotTile(ptClick);//plot tile ptPlot.x-=(Scroller.GetScreenSpaceWidth()/2);//adjust by half screenspace ptPlot.y-=(Scroller.GetScreenSpaceHeight()/2); Scroller.SetAnchor(&ptPlot);//set anchor Renderer.AddRect(Scroller.GetScreenSpace()); //return to GS_IDLE iGameState=GS_IDLE; }break; case GS_CLICKSTACK: { //prepare the stack int count; for(count=0;count<20;count++)//clear out the list SelectUnitList[count]=NULL; //reset count to 0 count=0; //iterate through the list of units at the current map location UNITLISTITER iter;//iterator PUNITINFO pUnitInfo;//unit info for(iter=mlMap[ptClick.x][ptClick.y].ulUnitList.begin();count<20 && iter!=mlMap[ptClick.x][ptClick.y].ulUnitList.end();iter++)//iterate through list { pUnitInfo=*iter;//grab the unit //place unit in list SelectUnitList[count]=pUnitInfo; //add 1 to count count++; } //send to next gamestate iGameState=GS_PICKUNIT; }break; case GS_PICKUNIT: { //no scrolling Renderer.ScrollFrame(0,0); //update frame Renderer.UpdateFrame(); //place select window onto display DDBLTFX ddbltfx; DDBLTFX_Clear(&ddbltfx); ddbltfx.dwFillColor=dwSelectWindowColor; lpddsBack->Blt(&rcSelectWindow,NULL,NULL,DDBLT_WAIT | DDBLT_COLORFILL,&ddbltfx); //show the units int cellx,celly;//cell position int cellnum;//number of the cell int pixelx,pixely;//pixel position for(celly=0;celly<4;celly++) { for(cellx=0;cellx<5;cellx++) { cellnum=cellx+celly*5;//calculate the cell number //check that the unit exists if(SelectUnitList[cellnum]) { //calculate pixel position pixelx=rcSelectWindow.left+ptCellSize.x*cellx; pixely=rcSelectWindow.top+ptCellSize.y*celly; //plot the units position pixelx+=ptUnitOffset.x; pixely+=ptUnitOffset.y; //put the unit tsUnit.PutTile(lpddsBack,pixelx,pixely,SelectUnitList[cellnum]->iType); //move the pixel to place shielf pixelx+=ptShieldOffset[SelectUnitList[cellnum]->iType].x; pixely+=ptShieldOffset[SelectUnitList[cellnum]->iType].y; //place appropriate shield if(SelectUnitList[cellnum]->bHolding) { //unit is holding tsShield.PutTile(lpddsBack,pixelx,pixely,iCurrentTeam*2+4); } else { //unit is not holding if(SelectUnitList[cellnum]->iMovePoints) { //unit has movement points left tsShield.PutTile(lpddsBack,pixelx,pixely,iCurrentTeam*2); } else { //unit does not have movememtn points left tsShield.PutTile(lpddsBack,pixelx,pixely,iCurrentTeam*2+8); } } } } } //flip lpddsMain->Flip(0,DDFLIP_WAIT); }break; } } void RenderFunc(LPDIRECTDRAWSURFACE7 lpddsDst,RECT* rcClip,int xDst,int yDst,int xMap,int yMap) { //put background tile tsBack.ClipTile(lpddsDst,rcClip,xDst,yDst,0); //check for an empty list if(!mlMap[xMap][yMap].ulUnitList.empty()) { //list is not empty UNITLISTITER iter=mlMap[xMap][yMap].ulUnitList.begin();//get iterator to beginning of list PUNITINFO pUnitInfo=*iter;//grab the item //if this is the current unit if(pUnitInfo==pCurrentUnit) { //this is the current unit if(!bFlash) return;//if flash is "off" dont render } tsUnit.ClipTile(lpddsDst,rcClip,xDst,yDst,pUnitInfo->iType);//place the unit iter++;//move to the next item in the list if(iter==mlMap[xMap][yMap].ulUnitList.end())//if the end of the list, this is a single unit { if(pUnitInfo->bHolding)//if holding position {//holding tsShield.ClipTile(lpddsDst,rcClip,xDst+ptShieldOffset[pUnitInfo->iType].x,yDst+ptShieldOffset[pUnitInfo->iType].y,pUnitInfo->iTeam*2+4);//place the shield } else {//not holding tsShield.ClipTile(lpddsDst,rcClip,xDst+ptShieldOffset[pUnitInfo->iType].x,yDst+ptShieldOffset[pUnitInfo->iType].y,pUnitInfo->iTeam*2);//place the shield } } else//more than one unit... this is a stack { if(pUnitInfo->bHolding)//if holding position {//holding tsShield.ClipTile(lpddsDst,rcClip,xDst+ptShieldOffset[pUnitInfo->iType].x,yDst+ptShieldOffset[pUnitInfo->iType].y,pUnitInfo->iTeam*2+5);//place the shield } else {//not holding tsShield.ClipTile(lpddsDst,rcClip,xDst+ptShieldOffset[pUnitInfo->iType].x,yDst+ptShieldOffset[pUnitInfo->iType].y,pUnitInfo->iTeam*2+1);//place the shield } } } } void SetupMiniMap()//does initial setup of the minimap { //create the surface lpddsMiniMap=LPDDS_CreateOffscreen(lpdd,160,80); //load in the tileset tsMiniMap.Load(lpdd,"minimapts.bmp"); //initialize the minimap arrays MiniMap=new int[MAPWIDTH*MAPHEIGHT]; for(int count=0;count<MAPWIDTH*MAPHEIGHT;count++) { MiniMap[count]=0; } //initialize iso components //tile plotter MiniTilePlotter.SetMapType(ISOMAP_DIAMOND); MiniTilePlotter.SetTileSize(4,2); //scroller //calc worldspace MiniScroller.CalcWorldSpace(&MiniTilePlotter,&tsMiniMap.GetTileList()[0].rcDstExt,MAPWIDTH,MAPHEIGHT); //set screen-space RECT rcMiniScreenSpace; SetRect(&rcMiniScreenSpace,0,0,160,80); MiniScroller.SetScreenSpace(&rcMiniScreenSpace); //set the anchor POINT ptAnchor; ptAnchor.x=MiniScroller.GetWorldSpace()->left; ptAnchor.y=MiniScroller.GetWorldSpace()->top; MiniScroller.SetAnchor(&ptAnchor); } void UpdateMiniMap()//updates the minimap array { //upate the minimap array to reflect game status for(int y=0;y<MAPHEIGHT;y++)//loop through rows { for(int x=0;x<MAPWIDTH;x++)//loop through columns { MiniMap[y*MAPWIDTH+x]=0; //if empty, place a zero if(mlMap[x][y].ulUnitList.empty()) { MiniMap[y*MAPWIDTH+x]=0; } else { //occupied by a player UNITLISTITER iter=mlMap[x][y].ulUnitList.begin(); PUNITINFO pUnitInfo=*iter; if(pUnitInfo!=pCurrentUnit || bFlash) MiniMap[y*MAPWIDTH+x]=1+pUnitInfo->iTeam;//put team info into minimap array } } } } void RedrawMiniMap()//redraws the minimap to reflect current gamestate { //redraw the entire minimap //in real code, you can keep two arrays //one valid this frame and one valid last frame //and then only blit the changes from last frame to this one //thus reducing the number of blits //clear out the minimap surface DDBLTFX ddbltfx; DDBLTFX_ColorFill(&ddbltfx,0); lpddsMiniMap->Blt(NULL,NULL,NULL,DDBLT_WAIT | DDBLT_COLORFILL,&ddbltfx); POINT ptPlot;//plotting point for(int y=0;y<MAPHEIGHT;y++)//loop through rows { for(int x=0;x<MAPWIDTH;x++)//loop through columns { //put the mini-tile ptPlot.x=x; ptPlot.y=y; //plot map position ptPlot=MiniTilePlotter.PlotTile(ptPlot); //convert to screen coords ptPlot=MiniScroller.WorldToScreen(ptPlot); tsMiniMap.PutTile(lpddsMiniMap,ptPlot.x,ptPlot.y,MiniMap[y*MAPWIDTH+x]); } } //show a rectangle around viewable area on minimap HDC hdc; lpddsMiniMap->GetDC(&hdc);//borrow the dc from the minimap surface //get the anchor point from the main scroller POINT ptAnchor; ptAnchor.x=Scroller.GetAnchor()->x; ptAnchor.y=Scroller.GetAnchor()->y; //scale down by a factor of 16(to go from a 64x32 tile to 4x2) ptAnchor.x/=16; ptAnchor.y/=16; //subtract the mini-scroller's upper left ptAnchor.x-=MiniScroller.GetWorldSpace()->left; ptAnchor.y-=MiniScroller.GetWorldSpace()->top; //move to position in minimap MoveToEx(hdc,ptAnchor.x,ptAnchor.y,NULL); //draw box, one line at a time LineTo(hdc,ptAnchor.x+30,ptAnchor.y);//30 is 480/16 LineTo(hdc,ptAnchor.x+30,ptAnchor.y+30); LineTo(hdc,ptAnchor.x,ptAnchor.y+30); LineTo(hdc,ptAnchor.x,ptAnchor.y); lpddsMiniMap->ReleaseDC(hdc);//restore the dc to the minimap } void ShowMiniMap()//shows the minimap on-screen { RECT rcSrc;//src blitting rect RECT rcDst;//destination blitting rect //set src rect SetRect(&rcSrc,0,0,160,80); //set dest rect CopyRect(&rcDst,&rcSrc); OffsetRect(&rcDst,480,0); //do the blit lpddsBack->Blt(&rcDst,lpddsMiniMap,&rcSrc,DDBLT_WAIT,NULL); } void DestroyMiniMap()//cleans up the minimap { //delete minimap array delete[] MiniMap; //destroy minimap surface if(lpddsMiniMap) { lpddsMiniMap->Release(); lpddsMiniMap=NULL; } //destroy minimap tileset tsMiniMap.Unload(); }